package breakthrough;

import game.GameState;

import java.awt.Point;
import java.util.Arrays;

public class Heuristic {

	/*
	 * Weights for : 
	 * Number of pawns
	 * pawns in row x
	 * protected pawns in row x
	 * pawns to attack in row x
	 */
	public double num_pawn_weight;
	public double[] pawns_in_row_weight;
	public double[] protect_pawns_weight;
	public double[] pawns_to_attack_weight;

	public Heuristic()
	{
		double [] vals = new double[25];
		Arrays.fill(vals,0.5);
		int boardSize = BreakthroughState.N;
		this.num_pawn_weight = vals[0];
		this.pawns_in_row_weight = new double[boardSize];
		this.pawns_to_attack_weight = new double[boardSize];
		this.protect_pawns_weight = new double[boardSize];
		for (int i = 0 ; i < 7 ; i++)
		{
			this.pawns_in_row_weight[i] = vals[i+1];
			this.pawns_to_attack_weight[i] = vals[i+1+boardSize + boardSize];
			this.protect_pawns_weight[i] = vals[i+1+boardSize];
		}
	}

	/**
	 * Calculates the current score of the game based on the parameters of the heuristic. Scored by HomeScore - AwayScore. Positive implies that the state favors the home team, negative if the state favors the away team, and 0 in the case of a draw
	 * @param state The current state of the game
	 * @return Score of the game
	 */
	public double calc(BreakthroughState brd)	{
		char[][] board = brd.board;
		
		char playerSym;
		char opponentSym;
		
		playerSym = BreakthroughState.homeSym;
		opponentSym = BreakthroughState.awaySym;
		
		System.out.println();
		
		
		// Game Status
		if (brd.status == GameState.Status.HOME_WIN) return Double.POSITIVE_INFINITY;
		else if (brd.status == GameState.Status.AWAY_WIN) return Double.NEGATIVE_INFINITY;
		
		int boardSize = BreakthroughState.N;

		// Home values
		int homeTotalPawns = 0;
		int[] homePawnsInRow = new int[boardSize];
		int[] homeProtectedPawnsInRow = new int[boardSize];
		int[] homePawnsToAttack = new int[boardSize];
		double home_score = 0;
		double home_total_pawns_score = 0;
		double home_pawns_in_row_score = 0;
		double home_protect_pawns_score = 0;
		double home_pawns_to_attack_score = 0;

		// Away values
		int awayTotalPawns = 0;
		int[] awayPawnsInRow = new int[boardSize];
		int[] awayProtectedPawnsInRow = new int[boardSize];
		int[] awayPawnsToAttack = new int[boardSize];
		double away_score = 0;
		double away_total_pawns_score = 0;
		double away_pawns_in_row_score = 0;
		double away_protect_pawns_score = 0;
		double away_pawns_to_attack_score = 0;

		// Calculate subscores
		for (int row = boardSize-1 ; row >= 0 ; row--)	{
			int oppDistFromGoal = boardSize - row - 1;
			for (int col = 0 ; col < boardSize ; col++)	{
				if (board[row][col] == playerSym)	{
					homeTotalPawns++;
					homePawnsInRow[row]++;
					homeProtectedPawnsInRow[row] += (piecesNearby(playerSym, playerSym, board, new Point(row,col)) != 0 ) ? 1 : 0;
					homePawnsToAttack[row] += piecesNearby(playerSym, opponentSym, board, new Point(row,col));

				}
				else if (board[row][col] == opponentSym)	{
					awayTotalPawns++;
					awayPawnsInRow[row]++;
					awayProtectedPawnsInRow[row] += (piecesNearby(opponentSym, opponentSym, board, new Point(row,col)) != 0 ) ? 1 : 0;
					awayPawnsToAttack[row] += piecesNearby(opponentSym, playerSym, board, new Point(row,col));
				}
			}
			home_pawns_in_row_score += pawns_in_row_weight[row] * homePawnsInRow[row];
			home_protect_pawns_score += protect_pawns_weight[row] * homeProtectedPawnsInRow[row];
			home_pawns_to_attack_score += pawns_to_attack_weight[row] * homePawnsToAttack[row];

			away_pawns_in_row_score += pawns_in_row_weight[oppDistFromGoal] * awayPawnsInRow[row];
			away_protect_pawns_score += protect_pawns_weight[oppDistFromGoal] * awayProtectedPawnsInRow[row];
			away_pawns_to_attack_score += pawns_to_attack_weight[oppDistFromGoal] * awayPawnsToAttack[row];
		}

		// Calculate scores
		home_total_pawns_score = (num_pawn_weight * homeTotalPawns);
		home_score = home_total_pawns_score + home_pawns_in_row_score + home_protect_pawns_score + home_pawns_to_attack_score;

		away_total_pawns_score = (num_pawn_weight * awayTotalPawns);
		away_score = away_total_pawns_score + away_pawns_in_row_score + away_protect_pawns_score + away_pawns_to_attack_score;

//		System.out.println(home_total_pawns_score + " " + home_pawns_in_row_score + " " + home_protect_pawns_score + " " + home_pawns_to_attack_score);
//		System.out.println(away_total_pawns_score + " " + away_pawns_in_row_score + " " + away_protect_pawns_score + " " + away_pawns_to_attack_score);

//		return home_total_pawns_score - away_total_pawns_score;
		return home_score-away_score;
	}
	
	public double calc(BreakthroughState curr, String str)
	{
		if (curr.status == GameState.Status.HOME_WIN) return Double.POSITIVE_INFINITY;
		else if (curr.status == GameState.Status.AWAY_WIN) return Double.NEGATIVE_INFINITY;
		
		int home = 0;
		int away = 0;
		for (int i = 0 ; i < BreakthroughState.N ; i++)
		{
			for (int j = 0 ; j < BreakthroughState.N ; j++)
			{
				if (curr.board[i][j] == BreakthroughState.homeSym) home++;
				if (curr.board[i][j] == BreakthroughState.awaySym) away++;
			}
		}
		return home - away;
	}

/*	
	private static String intArrayToStr(int[] ary)	{
		String a = "(";
		for (int i = 0 ; i < ary.length ; i++)	{
			a += ary[i];
			if (i != ary.length-1) a += ',';
		}
		a += ")";
		return a;
	}
*/

	private int piecesNearby(char playerSym, char lookingFor, char[][] board, Point pos)	{
		int count = 0;
		int dir;
		if (lookingFor == BreakthroughState.homeSym)	dir = -1;
		else dir = 1;

		try{
			if (board[pos.x+dir][pos.y-1] == lookingFor) {
				count++;
//				System.out.println("Piece at " + pos.toString() + "(" + playerSym + ") " + " near piece at " + (new Point(pos.x+dir, pos.y-1)).toString() + "(" + lookingFor + ")");
			}
		}catch(IndexOutOfBoundsException e) {}

		try{
			if (board[pos.x+dir][pos.y+1] == lookingFor) count++;
//			System.out.println("Piece at " + pos.toString() + "(" + playerSym + ") " + " near piece at " + (new Point(pos.x+dir, pos.y+1)).toString() + "(" + lookingFor + ")");
		}catch(IndexOutOfBoundsException e)	{}

		return count;
	}
	

	public String toString()
	{
		String ans = "";
		ans += num_pawn_weight + " ";
		for (int i =0  ; i < 7 ; i++)
			ans += pawns_in_row_weight[i] + " " ;
		for (int i =0  ; i < 7 ; i++)
			ans += protect_pawns_weight[i] + " " ;
		for (int i =0  ; i < 7 ; i++)
			ans += pawns_to_attack_weight[i] + " " ;
		return ans;
	}
}